/*
 * profusion.c
 *
 *  Created on: 17-03-2011
 *      Author: Jakub Bartodziej <jakub.bartodziej@students.mimuw.edu.pl>
 */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <gmp.h>

#include "time_span.h"

void time_span_init(time_span* pts) {
	mpz_inits(pts->size_scale, pts->time_scale, NULL);
	mpz_set_ui(pts->size_scale, 1);
	mpz_set_ui(pts->time_scale, 1);
}

void time_span_deinit(time_span* pts) {
	mpz_clears(pts->size_scale, pts->time_scale, NULL);
}

void time_span_start(time_span* pts) {
	clock_gettime(CLOCK_MONOTONIC, &pts->start);
}

void time_span_end(time_span* pts) {
	clock_gettime(CLOCK_MONOTONIC, &pts->end);
}

void time_span_set_size(time_span* pts, unsigned long size) {
	pts->size = size;
}

void time_span_scale_size(time_span* pts, unsigned long scalar) {
	mpz_mul_ui(pts->size_scale, pts->size_scale, scalar);
}

void time_span_scale_time(time_span* pts, unsigned long scalar) {
	mpz_mul_ui(pts->time_scale, pts->time_scale, scalar);
}

static void get_time_nanoseconds(mpz_t result, struct timespec* start,
		struct timespec* end) {
	mpz_t temporary_z;
	mpz_init(temporary_z);

	mpz_set_si(result, end->tv_sec - start->tv_sec);
	mpz_mul_si(result, result, 1000L * 1000L * 1000L);
	mpz_set_si(temporary_z, end->tv_nsec - start->tv_nsec);
	mpz_add(result, result, temporary_z);

	mpz_clear(temporary_z);
}

static void scale(mpz_t integer, mpz_t factor) {
	mpz_mul(integer, integer, factor);
}

static double numerator_denominator_to_double(mpz_t numerator,
		mpz_t denominator) {
	double result;
	mpq_t numerator_q, denominator_q, fraction;

	mpq_inits(numerator_q, denominator_q, fraction, NULL);

	mpq_set_z(numerator_q, numerator);
	mpq_set_z(denominator_q, denominator);
	mpq_div(fraction, numerator_q, denominator_q);

	result = mpq_get_d(fraction);

	mpq_clears(numerator_q, denominator_q, fraction, NULL);

	return result;
}

double time_span_time_to_double(time_span* pts) {
	double result;
	mpz_t time_nanoseconds, billion;

	mpz_inits(time_nanoseconds, billion, NULL);

	get_time_nanoseconds(time_nanoseconds, &pts->start, &pts->end);
	scale(time_nanoseconds, pts->time_scale);

	mpz_set_si(billion, 1000L * 1000L * 1000L);

	result = numerator_denominator_to_double(time_nanoseconds, billion);

	mpz_clears(time_nanoseconds, billion, NULL);

	return result;
}

double time_span_bandwidth_to_double(time_span* pts) {
	double result;
	mpz_t size, time;

	mpz_inits(size, time, NULL);

	mpz_set_ui(size, pts->size);
	scale(size, pts->size_scale);

	get_time_nanoseconds(time, &pts->start, &pts->end);
	scale(time, pts->time_scale);

	result = numerator_denominator_to_double(size, time);

	mpz_clears(size, time, NULL);

	return result;
}
